"""
安全相关模块（如认证、授权等）。
"""""
from email.message import Message

# src/core/security.py
import jwt
from datetime import datetime, timedelta
from src.core import Config, str_utils as stru, retMes
from src.models.sys_api_project import SysApiProject as sysApiProjectModels
from sqlalchemy.orm import Session, Query
from typing import Optional


def get_app_id(db: Session,
               api_key: Optional[str],
               language: Optional[str] = None
               ) -> str:
    """
       根据传入的 API Key 查询对应的 app_id（项目编号）。

       功能说明
       --------
       1. 校验：API Key 不能为空（None 或空字符串均视为缺失）。
       2. 查询：在 sys_api_project 表（sysApiProjectModels）中按 api_key 精确匹配。
       3. 安全：未命中或 key 非法时抛出业务异常，由全局异常处理器统一返回 401。
       4. 返回：命中后只取 app_id 字段，不返回整行对象，减少序列化开销。

       参数
       ----
       db : Session
           SQLAlchemy 会话，由 FastAPI Depends 注入。
       api_key : Optional[str]
           请求头中的 API Key；允许 None 以便在网关层统一处理缺失场景。

       返回
       ----
       str
           与 api_key 绑定的 app_id（业务方项目编号）。

       异常
       ----
       ValueError
           由 retMes.Raise 抛出，全局异常处理器会转译为 401 Unauthorized。
       """

    if stru.is_empty(api_key):  # None、"" 都拦住
        msg = retMes.Msg(db, "-1", code="9391", language=language).mes()
        retMes.Raise(message=msg).mes()
    api_project = (
        db.query(sysApiProjectModels)  # ← 这里传的是类，不是模块
        .filter(sysApiProjectModels.api_key == api_key)
        .first()
    )

    if api_project is None:
        msg = retMes.Msg(db, '-1', "9300", language, api_key).mes()
        retMes.Raise(message=msg).mes()
    api_id = api_project.id
    api_name = api_project.api_name
    if api_project.is_activate == 0:
        msg = retMes.Msg(db, api_id, "9301", language).mes()
        retMes.Raise(message=msg).mes()

    if api_project.deletion_mark == 1:
        msg = retMes.Msg(db, api_id, code="9303", language=language).mes()
        retMes.Raise(message=msg).mes()
    if api_project.disable_date and api_project.disable_date < stru.now():
        msg = retMes.Msg(db, api_id, code="9302", language=language).mes()
        retMes.Raise(message=msg).mes()

    return api_id, api_name


def generate_token(data: dict,
                   expires_delta: timedelta = None):
    to_encode = data.copy()
    if not expires_delta:
        expires_delta = timedelta(minutes=Config.ACCESS_TOKEN_EXPIRE_MINUTES)

    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, Config.SECRET_KEY, algorithm="HS256")
    return encoded_jwt


def verify_token(db: Session,
                 api_key: str,
                 token: str,
                 language: Optional[str] = None):
    if token is None:
        msg = retMes.Msg(db, "-1", code="9390", language=language).mes()
        retMes.Raise(message=msg).mes()

    app_id, api_name = get_app_id(db, api_key, language)
    try:
        payload = jwt.decode(token, Config.SECRET_KEY, algorithms=["HS256"])
        payload['app_id'] = app_id
        payload['api_key'] = api_key
        payload["language"] = language
        # print(stru.timestamp_to_date(payload['exp']))
        payload['exp'] = stru.timestamp_to_date(payload['exp'])
        return payload
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None
